Operacje We/Wy

Operacje we/wy nie s zdefiniowane w samym jzyku C++. Umoliwiaj je biblioteki.
Takie biblioteki standardowo doczane s przez producenta danego kompilatora.
Wyrnia si trzy typowe biblioteki:

- biblioteka stdio (standard input/output) - wykorzystywana przez programistw C.
Uywanie jej w C++ nie jest w dobrym stylu.
- biblioteka stream - stara wersja biblioteki iostream - wycofywana z kompilatorw.
- biblioteka iostream (input/output stream - strumie wejciowy/wyjciowy) -
Ta biblioteka jest zalecana do stosowania w programowaniu w C++.

W poniszym tekcie bdzie opisana biblioteka iostream. Aby przeprowadzi jakiekolwiek
operacje na plikach, trzeba do programu doczy pliki iostream.h, fstream.h. Najpierw jednak
wprowadzimy kilka niezbdnych poj:

Strumie

Wprowadzanie i wyprowadzanie informacji mona potraktowa jako strumie bajtw pyncy od
rda do ujcia. S cztery predefiniowane strumienie:

cout    - jest powizany ze standardowym wyjciem (ekranem)
cin     - jest powizany ze standardowym wejciem (klawiatura)
cerr    - jest powizany ze standardowym urzdzeniem, na ktre chcemy wypisywa komunikaty
o bdach (zwykle ekran). Strumie jest niebuforowany.
clog    - jak wyej, ale strumie jest buforowany.

Strumienie cout, cin, cerr, clog s obiektami klasy istream i klasy ostream, zdefiniowanych
w bibliotece iostream.h.

Operator << oraz operator >>

cin >> x;

Powysza instrukcja pozwala na wczytanie z klawiatury liczby x (ze standardowego wejcia
wyjmowany jest strumie bajtw i wstawiany do zmiennej x). Operator >> jest nazywany operatorem
ekstrakcji. Instrukcja

cout << "hello world!";

pozwala na wywietlenie na ekranie acucha znakw (acuch znakw wstawiany jest na standardowe
wyjcie). Operator << nazywa si operatorem wstawiania.

wiczenie: Przetestowa poniszy program.

#include <stdio.h>
main()
{
int i;
float f;
char tekst[80];

cout << "podaj liczbe int: ";
cin >> i;
cout << "podales: " << i << endl;

cout << "podaj liczbe float: ";
cin >> f;
cout << "podales: " << f << endl;

cout << "napisz wyraz: ";
cin >> tekst;
cout << "napisales: " << tekst << "\n";
}

Sterowanie formatem i flagi stanu formatowania

Przy operacjach na strumieniu uywane s pewne domniemania dotyczce formatu informacji
wstawianej, bd wyjmowanej ze strumienia. Jeli owe domniemania nam nie odpowiadaj, to
moemy je zmieni i od tej pory dany strumie bdzie wczytywa, lub wypisywa wedug nowych
zasad (wedug nowego formatu).

Biece zasady formatowania zapisane s w tak zwanych flagach stanu formatowania (format
state flags). Flagi te s poszczeglnymi bitami w sowie long i znajduj si w klasie
nazwanej ios (input output state), ktra jest dziedziczona przez klasy istream, oraz ostream.

// flagi stanu formatowania sa nastepujace
public:
enum {
    skipws      = 0x0001,   // przeskakuje biae znaki
    left        = 0x0002,   // wyrwnanie do lewej
    right       = 0x0004,   // wyrwnanie do prawej
    internal    = 0x0008,   // justowanie
    dec     = 0x0010,   // konwersja decymalna
    oct     = 0x0020,   //      oktalna
    hex     = 0x0040,   //      hexadecymalna
    showbase    = 0x0080,   // poka podstaw konwersji
    showpoint   = 0x0100,   // poka kropk dziesitn
    uppercase   = 0x0200,   // wielkie litery
    showpos = 0x0400,   // znak "+" w liczbach dodatnich
    scientific  = 0x0800,   // notacja naukowa
    fixed       = 0x1000,   // notacja zwyka
    unitbuf     = 0x2000,   // buforowanie
    stdio       = 0x4000,   // wsppraca z stdio
};

Klasa ios w przyblieniu wyglda nastpujco:

class ios {
    long flagi_stanu_formatowania;
public:
    long setf(long, long);
    long setf(long);
    long unsetf(long);

    int width(int);
    int precision(int);
        // i wiele innych
}

Jak wida, w klasie ios znajduj si rwnie funkcje skadowe. Za ich pomoc moemy mniej,
lub bardziej wygodnie posugiwa si flagami.

Poniewa standardowe strumienie cin, cout, clog, cerr s obiektami klasy istream lub ostream
i dziedzicz po klasie ios, wic funkcj skadow klasy ios mona wywoa nastpujco:

cout.funkcja_skladowa_klasy_ios();

czyli na przykad

cout.precision(x);

Przejdmy do omwienia poszczeglnych funkcji klasy ios. Jeli chcemy zmieni jak flag
formatowania, to moemy to zrobi posugujc si na przykad takimi funkcjami skadowymi
klasy ios jak:

long setf(long ktre_flagi);
long unsetf(long_ktore_flagi);

Funkcje jako rezultat zwracaj dotychczasow warto sowa stanu formatowania. Instrukcja

cin.setf(ios::skipws);

ustawi flag skipws odpowiadajc za ignorowanie biaych znakw przez strumie wejciowy cin.
Inne flagi nie zmieni si. Instrukcja

cin.unsetf(ios::skipws);

dziaa odwrotnie: flaga skipws zostanie skasowana.

wiczenie: Przetestowa dziaanie programu

#include <iostream.h>

main()
{
float x = 1175;
long stare_flagi;

    cout << x << endl;
    cout << "zapamietanie stanu formatowania\n";
    stare_flagi = cout.flags();

    cout.setf(ios::showpoint);
    cout << x << endl;

    cout.setf(ios::scientific, ios::floatfield);
    cout << x << endl;

    cout.setf(ios::uppercase);
    cout << x << endl;

    unsetf(ios::showpoint);
    cout << x << endl;

    cout << "powrot do starego sposobu formatowania\n";
    cout.flags(stare_flagi);
    cout << x << endl;
}

funkcja flags() nie wymaga komentarza. Natomiast naley skomentowa linijk

cout.setf(ios::scientific, ios::floatfield);

Ot niektre flagi nale do wsplnych grup zwanych polami. Na przykad flagi ios::dec,
ios::hex i ios::oct nale do wsplnej grupy zwanej polem ios::basefield. Jeli chcemy, by na
przykad strumie wyjciowy wypisywa liczby w zapisie szesnastkowym, to musimy nie tylko ustawi
flag ios::hex, ale te wyczyci flag ios::dec. Zapis

cout.setf(ios::hex);
cout.unsetf(ios::dec);

jest do niewygodny. Dziki temu, e flagi nale do wsplnego pola ios::basefield, moliwy
jest zapis

cout.setf(ios::hex, ios::basefield);

ktry ustawi flag ios::hex i skasuje wszystkie pozostae nalece do pola. Podobnie ma si
sprawa z flagami ios::scientific i ios::fixed - nale do wsplnego pola ios::floatfield;

Stan formatowania mona te zmienia wygodniej - za pomoc dodatkowych funkcji, np.:

int width(int);
int width();

int precision(int);
int precision();

in fill(char);
int fill();

wiczenie: Przetestowa dziaanie poniszego fragmentu programu; na przykadzie programu
zapozna si z dziaaniem funkcji precision() i fill()

int liczba = 107.51578;
cout << "Wypis:" << liczba << "\nWypis:";
cout.width(15);
cout << liczba << endl;

Operacje we/wy na plikach

Chcc czyta, bd zapisywa do plikw, mamy do dyspozycji klasy:

ofstream - output file stream - zapis do plikw,
ifstream - input file stream - odczyt z plikw,
fstream - file stream - oba powysze.

Aby mc posuy si tymi klasami naley do programu wczy plik nagwkowy tego
fragmentu biblioteki, czyli:

#include <fstream.h>

Klasa ofstream jest pochodn klasy ostream (klasa ofstream dziedziczy po klasie ostream),
a klasa ifstream jest pochodn klasy istream. Analogicznie klasa fstream jest pochodn
klasy iostream.

Tych faktw w zasadzie nie trzeba pamita, ale wany jest wniosek z nich pyncy. Skoro
te klasy s pochodnymi klas istream i ostream, to znaczy, e dziedzicz wszystkie cechy
i zachowania swych klas podstawowych. Oznacza to w praktyce, e wszystko, co powiedzielimy
do tej pory o strumieniach, obowizuje take tutaj. Konkretnie: moemy take uywa
operatorw << i >> i wczeniej omwionych funkcji. Rnica polega na tym, e tutaj strumienie
nie s predefiniowane - czyli zdefiniowane za nas przez kompilator. Musimy je wic sami
zdefiniowa. Zatem, aby czyta z pliku (lub pisa do niego) naley:

- zdefiniowa strumie, czyli stworzy obiekt klasy ifstream, ofstream, bd fstream,
- okreli strumieniowi z jakim konkretnie plikiem ma si komunikowa (przypisa strumie
do pliku) i otworzy ten plik,
- przeprowadzi operacje we/wy
- zlikwidowa strumie, gdy praca z plikiem jest zakoczona.

wiczenie: Przetestowa dziaanie poniszego programu

#include <iostream.h>
#include <fstream.h>

main()
{
ofstream osrodek;

    osrodek.open("ksiezyc.tmp");
    osrodek << "to takie cialo niebieskie";
    osrodek.close();
}

Otwieranie i zamykanie strumienia

Za otwieranie strumienia odpowiedzialna jest funkcja open.

void open(char* nazwa, int tryb = ***, int prot = filebuf::openprot);

Jeli chodzi o trzeci argument, to reguluje on takie sprawy, jak prawa dostpu do pliku
innych uytkownikw - nie bdziemy si tym zajmowa.

Pierwszy argument to oczywicie nazwa pliku.

Drugi parametr okrela tryb pracy z zadanym plikiem (czy bdzie to odczyt danych, czy zapis).
W miejsce gwiazdek naley wstawi odpowiedni tryb pracy. A oto nastpujce tryby:

ios::in - (input) Otwrz plik do czytania
ios::out - (output) Otwrz plik do zapisu
ios::ate - (at end) Otwrz plik i ustaw si na kocu zawartoci
ios::app - (append) Otwrz do dopisywania
ios::trunc - (truncate) Otwrz, a jeli plik istnieje - skasuj star zawarto
ios::oncreate - (o create) Otwrz jeli plik ju istnieje
ios:: noreplace - (no replace) Otwrz jeli plik nie istnieje
ios::binary - Tryb ma by binarny

Zamykamy strumie wykorzystujc funkcj close();

wiczenie: Przetestowa dziaanie poniszego programu

main()
{
int x,y,z;
fstream plik;
plik.open("tabela.tmp", ios::out|ios::trunc);
plik.width(10);
plik << 10 << " " << 20 << " " << 30;
plik.close();

plik.open("tabela.tmp", ios::in);
plik >> x >> y >> z;
plik.close();

cout << x << endl << y << endl << z << endl;
getch();
}

Wskaniki

Z kadym plikiem skojarzone s dwa wskaniki: wskanik get - wskazujcy na miejsce,
z ktrego czytamy w pliku oraz wskanik put - wskazujcy miejsce w pliku, gdzie zapisywane
s dane. Wskaniki te mona dowolnie pozycjonowa funkcjami seekg(), seekp(). Pierwsza
z nich pozycjonuje wskanik get, druga z nich pozycjonuje wskanik put. Dziki temu, w poczeniu
ze sposobami otwarcia pliku, moliwy jest jednoczesny odczyt i zapis do tego samego pliku
w dowolnych miejscach, wskazywanych przez wskaniki get i put.

wiczenie: Przetestowa dziaanie programu

main()
{
    char tekst[] = {"To jest pierwsza linia tekstu\n"
            "To jest druga linia tekstu\n"
            "A to trzecia i ostatnia linia tekstu\n"};

    fstream strum("wiersz.tmp", ios::in|ios::out);
    strum << tekst;

    strum.seekp(25);  //pozycjonowanie kursora pisania na literze 25
    strum << "ABCDE";

    strum.seekp(-6, ios::end); //pozycjonowanie kursora pisania na 6 literze od konca
    strum << "X";
}
